using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using Microsoft.PowerShell.CmdletManagement;
using System.Collections.Generic;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using System.Collections.ObjectModel;

public partial class _Default : System.Web.UI.Page 
{
    private static string DefaultServiceUri = "http://TODO_WEBSERVICE_HOST/cmdletdesigner/Cmdlet.asmx";
    private static string DefaultAllowedHosts = "localhost,TODO_WEBSERVICE_HOST,devserver1";
    private static string DefaultProjectText = "Select a project";
    private static string DefaultCmdletText = "Select a cmdlet";

    private string ServiceUri = DefaultServiceUri;

    protected void Page_Load(object sender, EventArgs e)
    {
        // Get the configured Service URI
        if(System.Configuration.ConfigurationManager.AppSettings["ServiceUri"] != null)
        {
            ServiceUri = System.Configuration.ConfigurationManager.AppSettings["ServiceUri"];
        }

        // If we have a ServiceUri override, use that
        string requestedServiceUri = Request.QueryString["ServiceUri"];
        if (! String.IsNullOrEmpty(requestedServiceUri))
        {
            Uri newServiceUri = new Uri(requestedServiceUri);
            string allowedHosts = DefaultAllowedHosts;

            // Get the allowed hosts
            if (System.Configuration.ConfigurationManager.AppSettings["AllowedHosts"] != null)
            {
                allowedHosts = System.Configuration.ConfigurationManager.AppSettings["AllowedHosts"];
            }

            foreach (string allowedHost in allowedHosts.Split(','))
            {
                if(newServiceUri.Host == allowedHost)
                {
                    ServiceUri = requestedServiceUri;
                    break;
                }
            }
        }

        // First step: If we're posting back from either a Project or
        // cmdlet change, accept those changes
        string project = Request.QueryString["Project"];
        string cmdlet = Request.QueryString["Cmdlet"];

        // See if this is a post-back from setting the project name
        string selectedProject = Project.SelectedValue;
        if ((selectedProject != DefaultProjectText) &&
            (selectedProject != project) &&
            (IsValidProjectName(selectedProject)))
        {
            // It is a post-back, so redirect with the project name in the
            // query string.
            string newUrl = String.Format("Default.aspx?Project={0}{1}",
                selectedProject,
                (ServiceUri == DefaultServiceUri) ? "" : "&ServiceUri=" + ServiceUri
                );
            Response.Redirect(newUrl);
            return;
        }

        // See if this is a post-back from setting the cmdlet name
        string selectedCmdlet = Cmdlet.SelectedValue;
        if ((selectedCmdlet != DefaultCmdletText) &&
            (IsValidCmdletName(selectedCmdlet)))
        {
            if(IsValidProjectName(project))
            {
                // It is a post-back, so redirect with the selected cmdlet name in the
                // query string.
                string newUrl = String.Format("Default.aspx?Project={0}&Cmdlet={1}{2}",
                    project,
                    selectedCmdlet,
                    (ServiceUri == DefaultServiceUri) ? "" : "&ServiceUri=" + ServiceUri
                    );
                Response.Redirect(newUrl);
            }
            return;
        }

        // they've specified both the project and cmdlet
        // parameters, so generate the report
        if (IsValidProjectName(project) && IsValidCmdletName(cmdlet))
        {
            // Create the runspace, and validate that we could load the snapin
            Runspace runspace = CreatePowerShellRunspace();

            if (runspace == null)
            {
                Response.Write("Could not create runspace. Ensure that the PsSpecCmdletManagement snapin" +
                    " is registered.");
                return;
            }

            // Create the pipeline
            using (Pipeline pipeline = runspace.CreatePipeline())
            {
                // And execute the Get-PsSpecCmdletDesignerSpecHtml script
                string commandPath = Server.MapPath("App_Data/Get-CmdletDesignerSpecHtml.ps1");

                Command reportCommand = new Command(commandPath);
                reportCommand.Parameters.Add("CmdletName", cmdlet);
                reportCommand.Parameters.Add("ProjectName", project);
                reportCommand.Parameters.Add("ServiceUri", ServiceUri);

                pipeline.Commands.Add(reportCommand);

                Collection<PSObject> results = pipeline.Invoke();
                HandleErrors(pipeline);

                // Send the HTML output to the client, and end the response
                string htmlOutput = (string)results[0].BaseObject;
                string viewAnotherUrl = String.Format("Default.aspx?Project={0}{1}",
                    project,
                    (ServiceUri == DefaultServiceUri) ? "" : "&ServiceUri=" + ServiceUri
                    );
                htmlOutput = htmlOutput.Replace("<VIEWANOTHER_REPLACEMENT>", viewAnotherUrl);

                Response.Write(htmlOutput);
                Response.End();
            }

            return;
        }

        // They are missing either the project name or cmdlet name in the
        // query string. Check to see if the project name is specifieds
        if(IsValidProjectName(project))
        {
            // They have a valid project name, but haven't yet specified a 
            // cmdlet name. Populate the project listbox and cmdlet listbox
            PopulateProjectListbox(project);
            PopulateCmdletsListbox(project);

            return;
        }
        // They haven't specified a project name
        else
        {
            PopulateProjectListbox(null);

            // Populate the cmdlets listbox with the default 
            // text
            Cmdlet.Items.Add(new ListItem(DefaultCmdletText));

            return;
        }
    }

    // Populate the cmdlets listbox with available cmdlets
    private void PopulateCmdletsListbox(string project)
    {
        // Populate the cmdlets listbox
        Cmdlet.Items.Add(new ListItem(DefaultCmdletText));

        // Invoke the Get-PsSpecCmdlet command
        GetPsSpecCmdletCommand psSpecCmdletCommand = new GetPsSpecCmdletCommand();
        psSpecCmdletCommand.ProjectName = project;
        psSpecCmdletCommand.ServiceUri = ServiceUri;

        foreach (PsSpecCmdlet result in psSpecCmdletCommand.Invoke())
        {
            Cmdlet.Items.Add(new ListItem(result.Verb + "-" + result.Noun));
        }

        if (Cmdlet.Items.Count == 1)
        {
            ProjectError.Visible = true;
        }
    }

    // Populate the projects listbox with available projects
    private void PopulateProjectListbox(string project)
    {
        // If they haven't specified a selected project, add a 
        // "Please select a project" text.
        if (project == null)
        {
            Project.Items.Add(new ListItem(DefaultProjectText));
            project = DefaultProjectText;
        }

        // Invoke the Get-PsSpecProject command
        GetPsSpecProjectCommand command = new GetPsSpecProjectCommand();
        command.ServiceUri = ServiceUri;

        foreach (PsSpecProject result in command.Invoke())
        {
            Project.Items.Add(new ListItem(result.Name));
        }

        // Select the project
        Project.SelectedValue = project;
    }

    // Verify the validity of a cmdlet name
    private static bool IsValidCmdletName(string cmdlet)
    {
        if (String.IsNullOrEmpty(cmdlet))
            return false;

        return System.Text.RegularExpressions.Regex.IsMatch(cmdlet, "^[-a-zA-Z0-9_ ]+$");
    }

    // Verify the validity of a project name
    private static bool IsValidProjectName(string project)
    {
        if (String.IsNullOrEmpty(project))
            return false;

        return System.Text.RegularExpressions.Regex.IsMatch(project, "^[-a-zA-Z0-9_ ]+$");
    }

    private int GetCmdletId(string project, string cmdlet)
    {
        return 13;
    }

    // Create the PowerShell runspace we use to invoke the script
    private Runspace CreatePowerShellRunspace()
    {
        RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
        PSSnapInException warning;

        try
        {
            rsConfig.AddPSSnapIn("PsSpecCmdletManagement", out warning);
        }
        catch (PSArgumentException)
        {
            return null;
        }

        Runspace runspace = RunspaceFactory.CreateRunspace(rsConfig);
        runspace.Open();
        return runspace;
    }

    // Handle errors from a pipeline execution
    private void HandleErrors(Pipeline pipeline)
    {
        if (pipeline.Error.Count > 0)
        {
            string errors = "";

            foreach (PSObject error in pipeline.Error.ReadToEnd())
            {
                errors += error.ToString();
            }

            Response.Write(errors);
        }
    }
}
